/*

	Hernan Rivas Acosta
		hernanrivasacosta@gmail.com

	Licenced under the Mozilla Public Licence
		http://www.mozilla.org/MPL/MPL-1.1.html

	Hosted on Google Code
		http://code.google.com/p/as3localization/

*/
package org.her.localization
{
	import flash.events.EventDispatcher;
	import flash.utils.Dictionary;

	import org.her.localization.utils.StringUtils;
	import org.her.localization.utils.events.LocalizationEvent;
	import org.her.localization.utils.parsers.BasicParser;
	import org.her.localization.utils.parsers.IParser;

	public class LocalizationManager extends EventDispatcher
	{

		/**
		 * The current LocalizationManager version. Formated: vMajor.Minor.Revision
		 */
		public static const VERSION:String = 'v0.9.8'; // TODO: keep up to date.

		/**
		 * The contact information.
		 */
		public static const ABOUT:String = 'LocalizationManager ' + VERSION + ' Contact at hernanrivasacosta@gmail.com';

		/**
		 * A special default value that tells the LocalizationManager to return the requested id instead of a predefined value if no localization or not language was found.
		 *
		 * @see #defNoLang
		 * @see #defNoText
		 */
		public static const RETURN_QUERY:String = 'RETURN_QUERY';

		private var _currLang:String;
			/**
			 * The current language used by the LocalizationManager.
			 */
			public function get currentLanguage ():String { return _currLang; }

		private var _defNoLang:String;
			/**
			 * The string the LocalizationManager shows if no localization was found for the specified language.
			 */
			public function get defNoLang ():String { return _defNoLang; }
			/**
			 * @private
			 */
			public function set defNoLang (v:String):void { _defNoLang = v; }
		private var _defNoText:String;
			/**
			 * The string the LocalizationManager shows if no text was found for the specified id in the current language.
			 */
			public function get defNoText ():String { return _defNoText; }
			/**
			 * @private
			 */
			public function set defNoText (v:String):void { _defNoText = v; }

		private var _langs:Dictionary;

		/**
		 * Constructor
		 *
		 * @param defNoLang The string the LocalizationManager shows if no localization was found for the specified language.
		 * @param defNoText The string the LocalizationManager shows if no text was found for the specified id in the current language.
		 *
		 * @see #defNoLang
		 * @see #defNoText
		 */
		public function LocalizationManager(defNoLang:String = null, defNoText:String = null)
		{
			_defNoLang = defNoLang;
			_defNoText = defNoText;

			init();
		}

		private function init ():void
		{
			_langs = new Dictionary ();
		}

		/**
		 * Add localization data in the form of an XML
		 *
		 * @param xml The XML that will be parsed.
		 * @param lang The language code of the XML, if null is passed, the parser will try to find it in the XML. Note that if it fails to do so, no data will be added.
		 * @param allowOverride When adding multiple XMLs for the same language, set to true if the last added data will override any previous data.
		 * @param customParser IParser instance used in for this XML, if none is specified, the default will be used.
		 *
		 * @see org.her.localization.utils.parsers.IParser
		 */
		public function addLocalization (xml:XML, lang:String = null, allowOverride:Boolean = true, customParser:IParser = null):void
		{
			var loc:Localization;

			loc = new Localization (customParser || new BasicParser (), allowOverride);
			loc.parseXML(xml);

			lang = lang || loc.id; // If the language was not forced, read ir from the XML.

			if (lang != null)
			{
				_currLang = _currLang || lang;

				if (_langs[lang])
				{
					Localization(_langs[lang]).addDefinitions(loc);
				}
				else
				{
					_langs[lang] = loc;
				}
			}
			else
			{
				trace (	'WARNING: LocalizationManager.addLocalization failed. ' +
						'Reason: no language was specified and no language was retrieved from the XML. Any valid parsed data will be discarded.');
			}

		}

		private function getLocalization (lang:String):Localization
		{
			return _langs[lang];
		}

		/**
		 * Change the current language. This will trigger the LocalizationEvent.CHANGED.
		 *
		 * @param lang The language to be set.
		 */
		public function setCurrentLanguage (lang:String):void
		{
			if (_currLang != lang)
			{
				_currLang = lang;
				notifyLocalization();
			}
		}

		/**
		 * Returns the localized string for the specified language.
		 *
		 * @param id The text id to look for.
		 * @param replaceObj A dictionary or an Array where the replaceable values are stored. If the value is an Array, all values between curly braces will be casted to integers.
		 * @param lang The desired language, if null the currentLanguage will be used.
		 *
		 * @return the localized string
		 *
		 * @see org.her.localization.utils.StringUtils#getReplaceFunction()
		 */
		public function getText (id:String, replaceObj:* = null, lang:String = null):String
		{
			var str:String;

			if (hasLocalization(lang || _currLang))
			{
				if (hasText(id))
				{
					return replaceObj? StringUtils.findAndReplace(getLocalization(lang || _currLang).getString(id), replaceObj) : getLocalization(lang || _currLang).getString(id);
				}
				else
				{
					return _defNoText == RETURN_QUERY? id : _defNoText;
				}
			}
			else
			{
				return _defNoLang == RETURN_QUERY? id : _defNoLang;
			}
		}

		/**
		 * Returns true if a text was found for the specified language.
		 *
		 * @param id The text id to look for.
		 * @param lang The desired language, if null the currentLanguage will be used.
		 *
		 * @return true if there is a text for the id.
		 */
		public function hasText (id:String, lang:String = null):Boolean
		{
			// Returns true if there is a text for the id in the specified language or the default one if none was specified.
			return hasLocalization(lang || _currLang) && _langs[lang || _currLang].getString(id) != null;
		}

		/**
		 * Returns true if the language was found.
		 *
		 * @param lang The language to look for.
		 *
		 * @return true if there is a a language binded with the id.
		 */
		public function hasLocalization (lang:String):Boolean
		{
			return _langs[lang] != null;
		}

		private function notifyLocalization ():void
		{
			dispatchEvent(new LocalizationEvent (LocalizationEvent.CHANGED, _currLang));
		}

	}
}